package start.spring.basic.log;

import java.text.ParseException;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;
import start.spring.basic.dynamic.DynamicKafkaDAO;
import start.spring.basic.exception.ExceptionUtil;
import start.spring.basic.exception.common.ExceptionInterface;
import start.spring.basic.util.common.CookieUtil;
import start.spring.basic.util.common.DateUtil;
import start.spring.basic.util.common.RequestIpUtil;
import start.spring.basic.util.common.StringUtil;

/**
 * ****************************************************************************
 * 公共web请求log日志统一处理类
 *
 * @author(作者)：xuyongyun	
 * @author(复审人)：	Administrator 2018年8月22日
 * @version(版本)1.0
 * @since 1.0
 ******************************************************************************
 */
@Slf4j
@Aspect
@Component
public class LogAspect {
	
	@Autowired
	HttpServletRequest request;
	
	@Autowired
	DynamicKafkaDAO kafkaDAO;
	
	/**
	 * 定义日志切入点
	 *	
	 * @param operation 业务类型
	 * @since           1.0
	 */
	@Pointcut(value="@annotation(logInfo)")
	public void serviceStatistics(ServiceLog logInfo) {
		
	}
	
	/**
	 * 日志记录方法环绕
	 *	
	 * @param pjp
	 * @param logInfo
	 * @throws Throwable
	 * @since           1.0
	 */
	@Around(value="serviceStatistics(logInfo)")
    public Object around(ProceedingJoinPoint pjp,ServiceLog logInfo) throws Throwable{
		Object obj;
		JSONObject log = null;
		try {
			log = this.doBefore(pjp,logInfo);
			obj = pjp.proceed();
			this.doAfterReturning(log,logInfo,obj);
		}catch(Throwable e) {
			doAfterThrowing(log,e);
			throw e;
		}
        return obj;
    }
	
	/**
	 * 环绕前执行的方法
	 *	
	 * @param 注解参数
	 * 
	 * @since           1.0
	 */
	private JSONObject doBefore(ProceedingJoinPoint pjp,ServiceLog logInfo) {
		
		JSONObject appLog = new JSONObject();
		
		JSONObject joinPointInfo = AspectUtil.getJoinPointInfo(pjp);
		
		appLog.put("systemType", logInfo.systemType());
		appLog.put("businessType", logInfo.businessType());
		appLog.put("url", request.getRequestURI());
		
		String starttime = DateUtil.format(new Date(), DateUtil.FORMAT_YMDHMSSS_L);
		appLog.put("startTime", starttime);
		
		
		String traceId = request.getHeader(LogConstant.REQUEST_KEY_TRACEID);
		if(StringUtil.isEmpty(traceId)) {
			appLog.put("traceId", "");
		}else{
			appLog.put("traceId", traceId);
		}
		
		String ip = RequestIpUtil.getRealip(request);
		if(StringUtil.isEmpty(ip)) {
			appLog.put("ip", "");
		}else{
			appLog.put("ip", ip);
		}
		
		//获取用户id，未指定用户唯一标识，从request中获取
		if(StringUtil.isEmpty(logInfo.userId())) {
			
			String userId = request.getParameter("userId");
			userId = CookieUtil.getCookie(request, "userId");
			String userName = CookieUtil.getCookie(request, "userName");
			if(StringUtil.isEmpty(userId)){
				appLog.put("userId", "");
				appLog.put("userName", "");
	        }else {
	        	appLog.put("userId", userId);
	        	appLog.put("userName", userName);
	        }
		}else {
			appLog.put("userId", logInfo.userId());
        	appLog.put("userName", "");
		}
		
		appLog.put("className", joinPointInfo.get("className"));
		appLog.put("methodName", joinPointInfo.get("methodName"));
		appLog.put("requestArgs", joinPointInfo.get("requestArgs"));
		
		appLog.put("operDetail", "");
		
		return appLog;
	}
	
	/**
	 * 环绕后执行的方法
	 * @param logInfo 
	 * @param obj 
	 *	
	 * @param 注解参数
	 * 
	 * @since           1.0
	 */
	private void doAfterReturning(JSONObject appLog, ServiceLog logInfo, Object obj) {
		appLog.put("code", "200");
		appLog.put("msg", "");
		String endtime = DateUtil.format(new Date(), DateUtil.FORMAT_YMDHMSSS_L);
		appLog.put("endTime", endtime);
		
		String starttime = appLog.get("startTime").toString();
		
		long clhs = 0;
		try {
			clhs = DateUtil.stringDateToMillis(endtime, DateUtil.FORMAT_YMDHMSSS_L) - 
					    DateUtil.stringDateToMillis(starttime, DateUtil.FORMAT_YMDHMSSS_L);
		} catch (ParseException e) {
			e.printStackTrace();
		}
		appLog.put("clhs", clhs);
		
		if(logInfo.recordResult()) {
			appLog.put("operResult", obj);
		}else {
			appLog.put("operResult", "设置不记录请求结果！");
		}
		log.info("request detail: {}", appLog);
		
		if(kafkaDAO.kafkaExsit(LogConstant.KAFKA_KEY_LOGS)) {
			kafkaDAO.send(LogConstant.KAFKA_KEY_LOGS, LogConstant.LOGS_TOPIC, appLog.toJSONString());
		}
	}
	
	/**
	 * 异常通知，在目标方法非正常结束，发生异常或者抛出异常时执行
	 *	
	 * @param logMap 日志信息集合
	 * @param e 待处理异常
	 * @since           1.0
	 */
	private void doAfterThrowing(JSONObject appLog,Throwable e) {
		if(appLog==null) {
			log.info("request error info: {}", ExceptionUtil.getExceptionDeteil(e,0),null);
		}else {
			if(e instanceof ExceptionInterface) {
				appLog.put("code", ((ExceptionInterface) e).getErrcode());
			}else {
				appLog.put("code", "400001");
			}
			
			appLog.put("msg", ExceptionUtil.getExceptionDeteil(e,800));
			String endtime = DateUtil.format(new Date(), DateUtil.FORMAT_YMDHMSSS_L);
			appLog.put("endTime", endtime);
			
			String starttime = appLog.get("startTime").toString();
			
			long clhs = 0;
			try {
				clhs = DateUtil.stringDateToMillis(endtime, DateUtil.FORMAT_YMDHMSSS_L) - 
						    DateUtil.stringDateToMillis(starttime, DateUtil.FORMAT_YMDHMSSS_L);
			} catch (ParseException e1) {
				e1.printStackTrace();
			}
			appLog.put("clhs", clhs);
			
			log.info("request detail: {}", appLog);
			
			if(kafkaDAO.kafkaExsit(LogConstant.KAFKA_KEY_LOGS)) {
				kafkaDAO.send(LogConstant.KAFKA_KEY_LOGS, LogConstant.LOGS_TOPIC, appLog.toJSONString());
			}
		}
	}
}
